有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java多个枚举与一个枚举

我正在看反应流规范的Publisher(AsyncIterablePublisher.java)的示例实现,突然发现了一些我不明白为什么会这样做的事情

static interface Signal {};
enum Cancel implements Signal { Instance; };
enum Subscribe implements Signal { Instance; };
enum Send implements Signal { Instance; };

现实一点,我并不像写这篇文章的那个人那样是一个高级程序员,我确信这样做是有原因的。但我也无法解释为什么这样做会比这样做更好(我本来会这样做的)

enum Signal {
  Cancel,
  Subscribe,
  Send;
}

有人能给我解释一下为什么会更好吗?优点/缺点


共 (5) 个答案

  1. # 1 楼答案

    对于AsyncIterablePublisher中的使用类型,这两种形式是等效的,可以说后者(一个枚举包含多个常量)更自然

    事实上,前者非常罕见。我可以看到一个支持使用它的论据(但它非常罕见,这意味着这一点通常不那么重要):当你在自己的枚举中定义每个常量时,你有机会定义不同的方法/字段,例如:

    enum Cancel implements Signal { Instance; };
    enum Send implements Signal { 
      Instance; 
      public void someSendSpecificMethod() { ... }
    }
    

    所以你现在可以做Send.Instance.someSendSpecificMethod()。非常尴尬,非常罕见

  2. # 2 楼答案

    不同之处在于,您可以在不更改原始枚举的情况下添加另一个Signal实例。您的代码可以处理信号实例,并且可以提供不同的类型。与类的接口相同,它提供了灵活性,但只有在需要时才有用。在你的例子中,我看不出有多大用处,因为接口是空的,而实现它的枚举中没有任何内容

    你可以在这个网站上查看一个很好的enum接口示例:

    http://www.selikoff.net/2011/06/15/java-enums-can-implement-interfaces/

  3. # 3 楼答案

    不要太严格,以下是我对这条代码的解释。让我们给罗兰的主人打电话

    首先,罗兰需要一个通用的界面来支持所有人

    static interface Signal {};
    
    ConcurrentLinkedQueue<Signal> inboundSignals = new ConcurrentLinkedQueue<Signal>();
    

    CancelSubscribeSend这样的信号都有相同的用途,它们是不可变的,并且经常出现,因此最好将它们实现为Joshua Bloch's Singleton

    enum Cancel    implements Signal { Instance; };
    enum Subscribe implements Signal { Instance; };
    enum Send      implements Signal { Instance; };
    

    另一种方法类似于你的建议和我最喜欢的:

    enum CommonSignals implements Signal{
    
        Cancel {
            @Override
            void debug() {
                System.out.println("Cancel");
            }
        },
    
        Subscribe {
            @Override
            void debug() {
                System.out.println("Subscribe");
            }
        },
    
        Send {
            @Override
            void debug() {
                System.out.println("Send");
            }
        };
    
        abstract void debug();
    
        [...] some other methods I could need in the future
    }
    

    正如您所看到的,这是一个不同的实现。但这一想法与singleton的想法是一样的

    我们继续前进,找到以下代码:

    static final class Request implements Signal {
        final long n;
        Request(final long n) { // every Request has different value of n
            this.n = n;
        }
    };
    

    由于inboundSignals可以包含多个Request对象,因此不可能将这种类型的信号实现为单例。因此,它不能是CommonSignals的成员或实现为enum


    结论

    罗兰使用了多种可能性中的一种来实现一个单例。我认为这更多的是一个品味的问题,如何做到这一点

  4. # 4 楼答案

    所以,因为我不知道应该批准哪一个答案,我把这段代码的作者发了邮件,问他对这段代码的看法。以下是对话(block的是他的,普通文本是我的):

    Hi Michael,

    那么,只有一个枚举有什么区别呢?您不会有三个单例,但仍然有三个唯一定义的对象:

    Sure, that could be done, but then I'd have to invent a name for it (like ConcreteSignal), my chosen encoding avoids that :)

    enum Signal {
      Cancel, Subscribe, Send;
    }
    

    我认为这也是线程安全的,如果你真的想拥有共享接口,比如你可以与对象共享,你可以这样做:

    enum ConcreteSignal implements Signal {
      Cancel,
      Subscribe,
      Send;
    }
    

    You could definitely do as you have done above, however I'd argue that they [Cancel, Subscribe, Send] are not more concrete than Request (which is also a signal): https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/examples/src/main/java/org/reactivestreams/example/unicast/AsyncIterablePublisher.java#L49

    Also, that could also encourage to use the methods of Enum to enumerate all possible Signals, which it couldn't, since Request is not a Signal. Makes sense?

    实际上,在Scala中,您更愿意使用对象来实现,但在Java中,很少看到类似于您的实现的东西。我只是好奇,我错过了Java中一些简洁的特性。但如果我理解正确的话,那是因为您更容易接受该实现,因为它更接近您的首选语言Scala

    I guess there's a bit of that to it, but the singleton-as-enum-pattern in Java is quite well-known, so the only "weird" thing would be to have a distinct "enum" for each of Cancel, Subscribe and Send, but as I explained previously, since Request cannot be encoded like that I opted for the more "Scala-y" version.

    So if "Request" would not need any parameters, I'd done as you suggested: enum Signal { Cancel, Request, Subscribe, Send }

    谜团解开了:-)

  5. # 5 楼答案

    我认为使用enum接口的唯一原因是,一些信号的数据与其他信号不同,因此他需要扩展enum类型以将数据附加到它